package sheeps

/*
#include <stdlib.h>
int init_sheeps_so();
int run_sheeps_so(int project_id, int group_id, int server, const char* configfile);

typedef void* HSOCKET;
typedef unsigned char PROTOCOL;

void	goapi_Log(void* proto, unsigned char level, const char* log);
long long	goapi_Microsecond();
vodi	goapi_UserActiveTimeOutStop(void* proto, int time);

const char*	goapi_ConfigGetString(const char* section, const char* key, const char* def);
int			goapi_ConfigGetNumber(const char* section, const char* key, int def);
char		goapi_ConfigGetBool(const char* section, const char* key, char def);

HSOCKET	goapi_SocketConnect(void* proto, const char* ip, int port, PROTOCOL protocol);
char	goapi_SocketSend(void* proto, HSOCKET hsock, const char* data, int len);
char	goapi_SocketClose(void* proto, HSOCKET hsock);

void	goapi_HsocketPeerAddrSet(HSOCKET hsock, const char* ip, int port);
void	goapi_HsocketPeerAddr(HSOCKET hsock, char* ip, size_t ipsz, int* port);
void	goapi_HsocketLocalAddr(HSOCKET hsock, char* ip, size_t ipsz, int* port);
int		goapi_GetHostByName(const char* name, const char* ip, size_t ipsz);

int		goapi_HsocketKcpCreate(HSOCKET hsock, int conv, int mode);
void	goapi_HsocketKcpNodelay(HSOCKET hsock, int nodelay, int interval, int resend, int nc);
void	goapi_HsocketKcpWndsize(HSOCKET hsock, int sndwnd, int rcvwnd);
int		goapi_HsocketKcpDebug(HSOCKET hsock, char* buf, int size);

char	goapi_PlayStop(void* proto, const char* err);
void 	goapi_PlayPause(void* proto);
void 	goapi_PlayNormal(void* proto);
void 	goapi_PlayFast(void* proto, char mode);
int		goapi_PlayStep(void* proto);
int		goapi_PlayStepSession(void* proto);
char* 	goapi_PlayStepNote(void* proto);
void 	goapi_PlayBack(void* proto, int index);
char	goapi_PlayAction(void* proto, int action);
void 	goapi_PlayNoStop(void* proto);
int		goapi_PlayOver(void* proto);

void	goapi_ReportOnline(void* proto);
void	goapi_ReportOffline(void* proto);
void 	goapi_ReportCounter(void* proto, const char* key, int val, int stype, int space_time);
void 	goapi_ReportApiResponse(void* proto, const char* api, int response_time);
void 	goapi_ReportApiSend(void* proto, const char* api, int flow, int count);
void 	goapi_ReportApiRecv(void* proto, const char* api, int flow, int count);
void	goapi_ReportOverMessage(void* proto, const char* msg);
*/
import "C"
import (
	"container/list"
	"syscall"
	"unsafe"
)

type TaskConfig struct {
	Task_id    int
	Project_id int
	Machine_id int
	Run_number int
	Parms      string
	user_list  *list.List
}

type BaseUserCall interface {
	set_handle(handle uintptr)
	EventStart(taskcfg *TaskConfig, user_number int)
	EventConnectOpen(ip string, port int, protocol int)
	EventConnectMade(hsock uintptr)
	EventConnectFailed(hsock uintptr, errno int)
	EventConnectSend(ip string, port int, data []byte, protocol int)
	EventConnectRecved(hsock uintptr, data []byte)
	EventConnectClose(ip string, port int, protocol int)
	EventConnectClosed(hsock uintptr, errno int)
	EventTimeOut()
	EventStop(errmsg string)
}

type BaseUser struct {
	user_handle uintptr
}

func (B *BaseUser) set_handle(handle uintptr) {
	B.user_handle = handle
}

func (B *BaseUser) Log(log_level int, msg string) {
	b_msg, _ := syscall.BytePtrFromString(msg)
	C.goapi_Log(unsafe.Pointer(B.user_handle), C.uchar(log_level), (*C.char)(unsafe.Pointer(b_msg)))
}

func (B *BaseUser) Microsecond() int64 {
	ret := C.goapi_Microsecond()
	return int64(ret)
}

func (B *BaseUser) UserActiveTimeOutStop(time int) {
	C.goapi_UserActiveTimeOutStop(unsafe.Pointer(B.user_handle), C.int(time))
}

func (B *BaseUser) ConfigGetString(section string, key string, def string) string {
	s_section, _ := syscall.BytePtrFromString(section)
	s_key, _ := syscall.BytePtrFromString(key)
	value := C.goapi_ConfigGetString((*C.char)(unsafe.Pointer(s_section)), (*C.char)(unsafe.Pointer(s_key)), (*C.char)(unsafe.Pointer(uintptr(0))))
	if uintptr(unsafe.Pointer(value)) == 0 {
		return def
	}
	return C.GoString(value)
}

func (B *BaseUser) ConfigGetNumber(section string, key string, def int) int {
	s_section, _ := syscall.BytePtrFromString(section)
	s_key, _ := syscall.BytePtrFromString(key)
	value := C.goapi_ConfigGetNumber((*C.char)(unsafe.Pointer(s_section)), (*C.char)(unsafe.Pointer(s_key)), C.int(def))
	return int(value)
}

func (B *BaseUser) ConfigGetBool(section string, key string, def bool) bool {
	s_section, _ := syscall.BytePtrFromString(section)
	s_key, _ := syscall.BytePtrFromString(key)
	def_value := 0
	if def {
		def_value = 1
	}
	value := C.goapi_ConfigGetBool((*C.char)(unsafe.Pointer(s_section)), (*C.char)(unsafe.Pointer(s_key)), C.char(def_value))
	return value != 0
}

func (B *BaseUser) SocketConnect(ip string, port int, protocol int) uintptr {
	s_ip, _ := syscall.BytePtrFromString(ip)
	ret := C.goapi_SocketConnect(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_ip)), C.int(port), C.uchar(protocol))
	return uintptr(unsafe.Pointer(ret))
}

func (B *BaseUser) SocketSend(hsock uintptr, data []byte, len int) int8 {
	ret := C.goapi_SocketSend(unsafe.Pointer(B.user_handle), (C.HSOCKET)(unsafe.Pointer(hsock)), (*C.char)(unsafe.Pointer(&data[0])), C.int(len))
	return int8(ret)
}

func (B *BaseUser) SocketClose(hsock uintptr) {
	C.goapi_SocketClose(unsafe.Pointer(B.user_handle), (C.HSOCKET)(unsafe.Pointer(hsock)))
}

func (B *BaseUser) SocketPeerAddrGet(hsock uintptr) (string, int) {
	bip := make([]byte, 40)
	var port int32 = 0
	C.goapi_HsocketPeerAddr((C.HSOCKET)(unsafe.Pointer(hsock)), (*C.char)(unsafe.Pointer(&bip[0])), 40, (*C.int)(unsafe.Pointer(&port)))
	return cstring(bip), int(port)
}

func (B *BaseUser) SocketLocalAddrGet(hsock uintptr) (string, int) {
	bip := make([]byte, 40)
	var port int32 = 0
	C.goapi_HsocketLocalAddr((C.HSOCKET)(unsafe.Pointer(hsock)), (*C.char)(unsafe.Pointer(&bip[0])), 40, (*C.int)(unsafe.Pointer(&port)))
	return cstring(bip), int(port)
}

func (B *BaseUser) SocketPeerAddrSet(hsock uintptr, ip string, port int) {
	s_ip, _ := syscall.BytePtrFromString(ip)
	C.goapi_HsocketPeerAddrSet(C.HSOCKET(unsafe.Pointer(hsock)), (*C.char)(unsafe.Pointer(s_ip)), C.int(port))
}

func (B *BaseUser) GetHostByName(name string) {
	s_name, _ := syscall.BytePtrFromString(name)
	bip := make([]byte, 40)
	C.goapi_GetHostByName((*C.char)(unsafe.Pointer(s_name)), (*C.char)(unsafe.Pointer(&bip[0])), C.ulong(40))
}

func (B *BaseUser) KcpCreate(hsock uintptr, conv int, stream int) {
	C.goapi_HsocketKcpCreate(C.HSOCKET(unsafe.Pointer(hsock)), C.int(conv), C.int(stream))
}

func (B *BaseUser) KcpNodelay(hsock uintptr, nodelay int, interval int, resend int, nc int) {
	C.goapi_HsocketKcpNodelay(C.HSOCKET(unsafe.Pointer(hsock)), C.int(nodelay), C.int(interval), C.int(resend), C.int(nc))
}

func (B *BaseUser) KcpWndsize(hsock uintptr, snwd int, rcwn int) {
	C.goapi_HsocketKcpWndsize(C.HSOCKET(unsafe.Pointer(hsock)), C.int(snwd), C.int(rcwn))
}

func (B *BaseUser) KcpDebug(hsock uintptr) string {
	debug := make([]byte, 256)
	C.goapi_HsocketKcpDebug(C.HSOCKET(unsafe.Pointer(hsock)), (*C.char)(unsafe.Pointer(&debug[0])), C.int(256))
	return cstring(debug)
}

func (B *BaseUser) PlayStop(err string) {
	err_msg, _ := syscall.BytePtrFromString(err)
	C.goapi_PlayStop(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(err_msg)))
}

func (B *BaseUser) PlayNoraml() {
	C.goapi_PlayNormal(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) PlayPause() {
	C.goapi_PlayPause(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) PlayFast(fast bool) {
	flag := 0
	if fast {
		flag = 1
	}
	C.goapi_PlayFast(unsafe.Pointer(B.user_handle), C.char(flag))
}

func (B *BaseUser) PlayStep() int {
	ret := C.goapi_PlayStep(unsafe.Pointer(B.user_handle))
	return int(ret)
}

func (B *BaseUser) PlayStepSession() int {
	key := C.goapi_PlayStepSession(unsafe.Pointer(B.user_handle))
	return int(key)
}

func (B *BaseUser) PlayStepNote() string {
	note := C.goapi_PlayStepNote(unsafe.Pointer(B.user_handle))
	return C.GoString(note)
}

func (B *BaseUser) PlayBack(step int) {
	C.goapi_PlayBack(unsafe.Pointer(B.user_handle), C.int(step))
}

func (B *BaseUser) PlayAction(action int) {
	C.goapi_PlayAction(unsafe.Pointer(B.user_handle), C.int(action))
}

func (B *BaseUser) PlayNoStop() {
	C.goapi_PlayNoStop(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) PlayOver() {
	C.goapi_PlayOver(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) ReportOnline() {
	C.goapi_ReportOnline(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) ReportOffline() {
	C.goapi_ReportOffline(unsafe.Pointer(B.user_handle))
}

func (B *BaseUser) ReportCounter(key string, value int, report_type int, space_time int) {
	s_key, _ := syscall.BytePtrFromString(key)
	C.goapi_ReportCounter(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_key)), C.int(value), C.int(report_type), C.int(space_time))
}

func (B *BaseUser) ReportAPIResponse(api string, response_time int) {
	s_api, _ := syscall.BytePtrFromString(api)
	C.goapi_ReportApiResponse(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_api)), C.int(response_time))
}

func (B *BaseUser) ReportAPISend(api string, flow int, count int) {
	s_api, _ := syscall.BytePtrFromString(api)
	C.goapi_ReportApiSend(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_api)), C.int(flow), C.int(count))
}

func (B *BaseUser) ReportAPIRecv(api string, flow int, count int) {
	s_api, _ := syscall.BytePtrFromString(api)
	C.goapi_ReportApiRecv(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_api)), C.int(flow), C.int(count))
}

func (B *BaseUser) ReportOverMessage(message string) {
	s_msg, _ := syscall.BytePtrFromString(message)
	C.goapi_ReportOverMessage(unsafe.Pointer(B.user_handle), (*C.char)(unsafe.Pointer(s_msg)))
}

func cstring(p []byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

func uintptr_2_string(ptr uintptr) string {
	data := make([]byte, 0)
	p := (*byte)(unsafe.Pointer(ptr))
	for *p != 0 {
		data = append(data, *p)
		ptr++
		p = (*byte)(unsafe.Pointer(ptr))
	}
	return string(data)
}

func uintptr_2_bytes(ptr uintptr, len int) []byte {
	data := make([]byte, len)
	var p *byte
	for i := 0; i < len; i++ {
		p = (*byte)(unsafe.Pointer(ptr))
		data[i] = *p
		ptr++
	}
	return data
}

// callback 必须要有返回值，不然会报错
//
//export event_start_callback
func event_start_callback(U *BaseUserCall, task_id C.int, user_number C.int) {
	(*U).EventStart(task_all[int(task_id)], int(user_number))
}

//export event_connect_open_callback
func event_connect_open_callback(U *BaseUserCall, ip uintptr, port int, protocol int) {
	g_ip := uintptr_2_string(ip)
	(*U).EventConnectOpen(g_ip, port, protocol)
}

//export event_connect_made_callback
func event_connect_made_callback(U *BaseUserCall, hsock uintptr) {
	(*U).EventConnectMade(hsock)
}

//export event_connect_failed_callback
func event_connect_failed_callback(U *BaseUserCall, hsock uintptr, errno int) {
	(*U).EventConnectFailed(hsock, errno)
}

//export event_connect_send_callback
func event_connect_send_callback(U *BaseUserCall, ip uintptr, port int, data uintptr, len int, protocol int) {
	g_ip := uintptr_2_string(ip)
	g_data := uintptr_2_bytes(data, len)
	(*U).EventConnectSend(g_ip, port, g_data, protocol)
}

//export event_connect_recved_callback
func event_connect_recved_callback(U *BaseUserCall, hsock uintptr, data uintptr, len int) {
	g_data := uintptr_2_bytes(data, len)
	(*U).EventConnectRecved(hsock, g_data)
}

//export event_connect_close_callback
func event_connect_close_callback(U *BaseUserCall, ip uintptr, port int, protocol int) {
	g_ip := uintptr_2_string(ip)
	(*U).EventConnectClose(g_ip, port, protocol)
}

//export event_connect_closed_callback
func event_connect_closed_callback(U *BaseUserCall, hsock uintptr, errno int) {
	(*U).EventConnectClosed(hsock, errno)

}

//export event_timeout_callback
func event_timeout_callback(U *BaseUserCall) {
	(*U).EventTimeOut()
}

//export event_stop_callback
func event_stop_callback(U *BaseUserCall, err uintptr) {
	errmsg := uintptr_2_string(err)
	(*U).EventStop(errmsg)
}

var task_all = make(map[int]*TaskConfig, 256)

var task_start_callback_func func(*TaskConfig)
var task_stop_callback_func func(*TaskConfig)
var create_user_callback_func func(uintptr) BaseUserCall

//export Task_start_callback
func Task_start_callback(task_id int, project_id int, machine_id int, run_number int, parms *C.char) {
	taskcfg := new(TaskConfig)
	taskcfg.Task_id = task_id
	taskcfg.Project_id = project_id
	taskcfg.Machine_id = machine_id
	taskcfg.Run_number = run_number
	taskcfg.Parms = C.GoString(parms)
	taskcfg.user_list = list.New()
	task_all[int(task_id)] = taskcfg
	if task_start_callback_func != nil {
		task_start_callback_func(taskcfg)
	}

}

//export Task_stop_callback
func Task_stop_callback(task_id C.int) {
	if task_stop_callback_func != nil {
		task_stop_callback_func(task_all[int(task_id)])
	}
	delete(task_all, int(task_id))
}

//export Task_user_create_callback
func Task_user_create_callback(user_handle uintptr, ud *unsafe.Pointer, task_id int) {
	user := create_user_callback_func(user_handle)
	user.set_handle(user_handle)
	ptr := &user
	task_all[task_id].user_list.PushBack(ptr)
	*ud = unsafe.Pointer(ptr)
}

func init_engin(create_user func(uintptr) BaseUserCall, task_start func(*TaskConfig), task_stop func(*TaskConfig)) {
	create_user_callback_func = create_user
	task_start_callback_func = task_start
	task_stop_callback_func = task_stop
	C.init_sheeps_so()
}

func start_engin(project_id int, group_id int, local_server bool, config_file string) {
	configfile, _ := syscall.BytePtrFromString(config_file)
	localserver := 0
	if local_server {
		localserver = 1
	}
	C.run_sheeps_so(C.int(project_id), C.int(group_id), C.int(localserver), (*C.char)(unsafe.Pointer(configfile)))
}

func LoadEngin(project_id int, group_id int, local_server bool, config_file string, create_user func(uintptr) BaseUserCall) {
	//func LoadEngin(project_id int, group_id int, local_server bool, config_file string, create_user func(uintptr) BaseUserCall, task_start func(*TaskConfig), task_stop func(*TaskConfig)) {
	init_engin(create_user, nil, nil)
	start_engin(project_id, group_id, local_server, config_file)
}
